package com.gjy.sb2.advice;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gjy.sb2.annotation.Decrypt;
import com.gjy.sb2.domain.AjaxRequest;
import com.gjy.sb2.domain.exception.DecoderException;
import com.gjy.sb2.domain.message.DecryptedInputMessage;
import com.gjy.sb2.sm.SmUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-07-08 09:07:48
 */
@RestControllerAdvice
public class Sm2DecryptAdvice implements RequestBodyAdvice {

    private static final Logger log = LoggerFactory.getLogger(Sm2DecryptAdvice.class);

    @Value("${sm2.pubK}")
    private String pubK;
    @Value("${sm2.priK}")
    private String priK;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return methodParameter.hasMethodAnnotation(Decrypt.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage message, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> cls) throws IOException {
        try {
            String data = StreamUtils.copyToString(message.getBody(), StandardCharsets.UTF_8);
            ObjectMapper om = new ObjectMapper();
            AjaxRequest ar = om.readValue(data, AjaxRequest.class);
            log.info("ar: {}", ar);
            String encrypt = ar.getEncrypt();
            String decrypt = SmUtil.sm2Decrypt(priK, encrypt);
            boolean verify = SmUtil.sm2Verify(pubK, decrypt, ar.getSign());
            if (verify) {
                return new DecryptedInputMessage(decrypt, message.getHeaders());
            } else {
                log.error("签名验证未通过: {},{}", encrypt, decrypt);
                throw new DecoderException("签名验证未通过");
            }
        } catch (Exception e) {
            log.error("解密失败: {}", e.getMessage());
            throw new DecoderException(e);
        }
    }

    @Override
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> cls) {
        return o;
    }

    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> cls) {
        return null;
    }
}
